Skip to content

feat(get-state-by-cep): add getStateByCep, the state that owns a CEP range - #562

Open
hyanmandian wants to merge 3 commits into
claude/get-cnpj-infofrom
claude/get-state-by-cep
Open

hyanmandian wants to merge 3 commits into
claude/get-cnpj-infofrom
claude/get-state-by-cep

Conversation

@hyanmandian

@hyanmandian hyanmandian commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Update (2026-09-27)

  • getMunicipalityByCep was removed from this PR and from its history: the maintainer decided to ship it later, generated from an official Correios export. The community gist its table was generated from (pinned revision of hugosenari/ec1a7d88…) turned out to be out of date: 406 ranges started at -001 instead of -000 (for example getMunicipalityByCep("01000-000") was null for São Paulo), and 135 municipalities differed from the current Correios faixas after the recodings since 2020 (for example 33230-000 read as Vespasiano instead of Lagoa Santa). Its range table, its generator (scripts/municipality-cep-ranges.ts) and the scripts/data.ts step that ran it, the OTHER_NAMES spellings, its docs, CONTRIBUTING notes, JSR entry, API report line and bundle-size rows went with it. What stays: getStateByCep, the shared _internals/find-cep-range lookup, and the rename of the internal IBGE municipality table cities.ts → municipalities.ts.

Stacked on #560. This PR sits on top of #560 (getCnpjInfo) and merges after it, which in turn sits on #559 and #558. Its base branch is claude/get-cnpj-info, so the diff shown here is the getStateByCep changes alone. Part of stack #591.

What

One offline CEP range lookup, plus one naming cleanup:

  • getStateByCep: the state (UF) that owns a CEP, from the CEP ranges the Correios assign to each state. A 30 row pure literal in src/get-state-by-cep/constants.ts.
  • The validate-parse-find logic lives in a new internal, findCepRange (src/_internals/find-cep-range/find-cep-range.ts), so a later CEP range lookup can reuse it.
  • Renamed the internal src/_internals/constants/cities.ts to municipalities.ts: it exports Municipality, not City, and nothing public imports the path directly, so this is not a breaking change. No public export is renamed.

No network call: it answers from a range table, not a CEP API.

API

getStateByCep(value: string | number): State | null
  • Accepts what isValidCep accepts: 8 digits, string or number, with spaces, dots and hyphens ignored. A number must be a non-negative integer (isLookupCode), so -20040020 and 2004002.5 are null instead of being read as a CEP. A CEP starting with 0 has to be a string, as in isValidCep.
  • Returns the same State object as getStates / getStateByIbgeCode (a fresh copy), so it composes with the rest of the state family.
  • null for an invalid CEP, for any non string/number input, and for a CEP outside every range.
getStateByCep("01310-100"); // { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 }
getStateByCep(20040020)?.code; // "RJ"
getStateByCep("69300-000")?.code; // "RR" (inside the Amazonas block)
getStateByCep("72800-000")?.code; // "GO" (inside the Distrito Federal block)
getStateByCep("78900-000"); // null (no state owns 78900-000 to 78999-999)

Ranges in the getStateByCep table (three states have two):

UF Range UF Range
SP 01000-000 to 19999-999 AM 69000-000 to 69299-999 and 69400-000 to 69899-999
RJ 20000-000 to 28999-999 RR 69300-000 to 69399-999
ES 29000-000 to 29999-999 AC 69900-000 to 69999-999
MG 30000-000 to 39999-999 DF 70000-000 to 72799-999 and 73000-000 to 73699-999
BA 40000-000 to 48999-999 GO 72800-000 to 72999-999 and 73700-000 to 76799-999
SE 49000-000 to 49999-999 RO 76800-000 to 76999-999
PE 50000-000 to 56999-999 TO 77000-000 to 77999-999
AL 57000-000 to 57999-999 MT 78000-000 to 78899-999
PB 58000-000 to 58999-999 MS 79000-000 to 79999-999
RN 59000-000 to 59999-999 PR 80000-000 to 87999-999
CE 60000-000 to 63999-999 SC 88000-000 to 89999-999
PI 64000-000 to 64999-999 RS 90000-000 to 99999-999
MA 65000-000 to 65999-999 PA 66000-000 to 68899-999 (a single range today)
AP 68900-000 to 68999-999

Sources

The 30 getStateByCep boundaries are cross-verified

They do not rest on the tamnil gist alone. Every boundary was re-derived from sources independent of it, and all 30 match the table:

  • The official "Localidades alvo" PDF above, re-extracted independently: same 10,189 CEPs, same single typo, no contradiction. It pins most boundaries tightly (for example DF 72774-999 against GO 72800-001, GO 72979-999 against DF 73000-001, MT 78899-999).
  • The hugosenari community CSV of per-municipality Correios CEP ranges (https://gist.github.com/hugosenari/ec1a7d88f5bdd01844424dbc9aff9590, 5,764 rows): mapping both endpoints of every row through this 30 row UF table gives 0 mismatches over 11,528 endpoints, and the CSV pins each of the 30 boundaries to the adjacent CEP the same way the PDF does (for example PA 68899-999 against AP 68900-001). It is out of date at the municipality level (see the Update above), but its state boundaries agree with every other source.
  • Live ViaCEP lookups (a DNE mirror) on the boundaries the PDF leaves loose: 69399-000 Cantá/RR and 69400-970 Manacapuru/AM, 76801-000 Porto Velho/RO, 73700-000 Padre Bernardo/GO, 72800-010 Luziânia/GO, 68890-000 Afuá/PA, 68900-010 Macapá/AP, 79002-000 Campo Grande/MS.
  • Five further independent range tables agree on all 30 rows: JoseQuintas/sefazclass (json/sefazcepuf.json), klawdyo/validation-br (src/cep.ts), pdrodavi/cep2uf, and the carrier and e-commerce tables of bring.com.br, blog.shoppub.com.br and ajuda.lojaintegrada.com.br.
  • The table has no overlap and exactly the two gaps it documents (00000-000 to 00999-999 and 78900-000 to 78999-999).

Two secondary sources disagree, and both are wrong:

  • MT ends at 78899-999, not 78999-999. The Wikipedia zone table lists "MT Interior | 78110 - 78999", which would close the 789xx gap. The official PDF's highest MT CEP is 78899-999 (Sorriso) and it lists no CEP at all in 789xx; the hugosenari CSV has none of its municipality ranges there either; live ViaCEP answers {"erro":true} for 78900-000, 78950-000 and 78999-000. 789xx is the range Rondônia vacated when it was moved to 768xx, which is the same move that took Goiás down to 76799-999. The gap stays.
  • SP is one range, not two. datasets-br/state-codes records SP as 01000-000–09999-999 plus 11000-000–19999-999. The Correios UF faixa, which is what this table copies, gives SP a single 01000-000–19999-999, and so does every other source checked. getStateByCep("10000-000") therefore answers SP although no city uses 10xxx: the São Paulo capital ranges skip over 10xxx, the "Localidades alvo" PDF has no CEP starting with 10 anywhere in its 76 pages, and live ViaCEP answers {"erro":true} for 10000-000. A faixa is the block the state owns, not a guarantee that every CEP in it is in use, and SP has other unused blocks. The JSDoc and both docs say so.

Naming: the internal municipality table

Looked at git log, the JSDoc of getCities/getMunicipality and CONTRIBUTING.md; there is no mention of a planned rename of any public export. Commit d313bcc2 (docs(municipalities): deprecate getCities and getMunicipality in favour of the municipality family) already settled the public naming on main:

The municipality is the entity of the Constituição (art. 18) and of the IBGE dataset the library ships, so getMunicipalities and getMunicipalityByCode are the API. getCities (names only) and the asynchronous getMunicipality of 2.3.0 keep working unchanged and are marked deprecated, to be removed in the next major; matching a municipality by name, which getMunicipality also did, is left to the application over getMunicipalities, since names vary in ways no library rule settles (abbreviations, former names, hyphens, typos).

So no public export is renamed in this PR (that would be the exact breaking change the release must avoid). What did move: the internal _internals/constants/cities.ts to municipalities.ts, since it exports Municipality, is behind getMunicipalities/getMunicipalityByCode/getMunicipality/getCities alike, and nothing public imports its path. The generated file is unchanged apart from its path; scripts/cities.ts, scripts/data.ts and scripts/data-summary.ts point at the new path.

Verification

  • npm run check: pass
  • npm run test -- --run: pass
  • npm run test:coverage: 100% statements, branches, functions and lines
  • npm run build, npm run check:api:update: pass, report committed (one new export: getStateByCep; Municipality re-exported from its new path)
  • npm run check:unused: pass
  • npm run check:duplication: 0 clones
  • npm run check:tree-shaking: getStateByCep 4620 B / 1565 B gzip
  • npm run check:commits: pass
  • npm run test:mutation -- --mutate 'src/_internals/find-cep-range/find-cep-range.ts': 100% (17 killed, 0 survived)
  • npm run test:mutation -- --mutate 'src/get-state-by-cep/get-state-by-cep.ts': 100% (9 killed, 0 survived)
  • bun test src and npm run test:deno: pass
  • npm run build:docs and npm run build:jsr: run, output committed (jsr.json gains ./get-state-by-cep)
  • Browser test scripts and the full Stryker run were not run locally; CI runs both.

Open points

  • The getStateByCep ranges were not read from the official Correios search itself, because it is behind a CAPTCHA that I did not try to bypass. They come from third party copies of that search, corroborated as described above.
  • The table is hand written in src/get-state-by-cep/constants.ts, with no generator under scripts/, because the only official source cannot be fetched by a script.
  • Pará is a single range today (66000-000 to 68899-999); the task mentioned it as historically split, and no source checked lists more than one range for it.
  • isValidCep reads -20040020 and 2004002.5 as valid CEPs today; getStateByCep rejects them as numbers (they stay accepted as strings such as "20040-020"). That util was left untouched.

Rebase onto #560

Rebased from main onto claude/get-cnpj-info, so this branch now carries #558, #559 and #560 underneath it. Conflicts resolved:

  • docs/llms.txt and docs/llms-full.txt are no longer tracked (they are generated now), so both were git rm-ed.
  • The getStateByCep section of docs/utilities.md and docs/pt-br/utilities.md was ported into the new per-utility format of feat: Standard Schema wrapper, JSR, pkg.pr.new, docs previews and a playground #556: a short paragraph, a bullet list for the accepted input and the edge cases, the javascript block, and a Source: line pointing at the Correios "Busca Faixa de CEP".
  • src/index.ts and src/index.test.ts kept strictly alphabetical, between getPixPayloadInfo and getStateByIbgeCode.
  • jsr.json (new on main) regenerated with npm run build:jsr, and reports/api/brazilian-utils.api.md with npm run check:api:update. Both are folded into the commits that own them, with no separate "regenerate" commit.

Re-verified on the rebased branch: npm run check, npm run test:coverage (100% statements, branches, functions and lines), npm run build, npm run check:unused, npm run check:duplication and npm run check:commits all pass.

Summary by CodeRabbit

  • New Features

    • Added offline CEP lookups for Brazilian states and municipalities.
    • Supports formatted CEP strings and non-negative integer inputs, including punctuation.
    • Returns matching state or municipality details for recognized CEP ranges, and null for invalid, uncovered, or unsupported ranges.
    • Added both functions to the public package API.
  • Documentation

    • Added usage details and API references in English and Portuguese documentation.
    • Updated municipality terminology and bundle-size information.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: f1e523bb-80c1-46e3-a559-2d22a49a7638

📥 Commits

Reviewing files that changed from the base of the PR and between 8c13c81 and 2b00546.

📒 Files selected for processing (6)
  • docs/pt-br/utilities.md
  • docs/utilities.md
  • jsr.json
  • reports/api/brazilian-utils.api.md
  • src/index.test.ts
  • src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/index.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

Changes

CEP state lookup

Layer / File(s) Summary
Lookup ranges and implementation
src/get-state-by-cep/constants.ts, src/get-state-by-cep/get-state-by-cep.ts
Adds ordered Correios ranges and getStateByCep, which validates, parses, matches, and resolves CEP values to cloned state data.
Lookup validation and coverage tests
src/get-state-by-cep/get-state-by-cep.test.ts
Tests formats, boundaries, gaps, invalid values, defensive copying, property behavior, and TypeScript types.
Public API and documentation
src/index.ts, jsr.json, reports/api/brazilian-utils.api.md, src/index.test.ts, docs/utilities.md, docs/pt-br/utilities.md
Exports getStateByCep, updates the API surface, and documents its inputs, range behavior, and return values.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant getStateByCep
  participant CEP_RANGES
  participant DATA
  Caller->>getStateByCep: Submit string or number CEP
  getStateByCep->>CEP_RANGES: Match the parsed CEP
  CEP_RANGES-->>getStateByCep: Return StateCode or no match
  getStateByCep->>DATA: Resolve state metadata
  DATA-->>getStateByCep: Return state data
  getStateByCep-->>Caller: Return cloned State or null
Loading

Suggested reviewers: claude

Merge Risk: ⚪ Minimal · up to 2b005

The new offline CEP lookup maps supported ranges to state data and returns null for invalid or uncovered values; its public API and documentation are aligned, so it is ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 5…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding getStateByCep to return the state that owns a CEP range.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Tree-shaking report

✅ No size regression. 1 new out of 163 exports.

Base Head Δ
Pre-existing exports, all imported 650.1 KB 650.1 KB (gzip 166.6 KB) +4 B (+0.0%)
Full import 650.1 KB 651.4 KB (gzip 166.8 KB) +1.3 KB (+0.2%)
Exports 162 163 +1

What changed (1)

Export Base Head Δ gzip
🆕 getStateByCep — 4.5 KB new 1.5 KB
All exports (163)
Export Base Head Δ gzip
⚪ GetAddressInfoByCepError 966 B 966 B 0 B 600 B
⚪ GetAddressInfoByCepNotFoundError 1.0 KB 1.0 KB 0 B 618 B
⚪ GetAddressInfoByCepServiceError 1.0 KB 1.0 KB 0 B 617 B
⚪ GetAddressInfoByCepValidationError 1.0 KB 1.0 KB 0 B 620 B
⚪ GetCepInfoByAddressError 966 B 966 B 0 B 600 B
⚪ GetCepInfoByAddressNotFoundError 1.0 KB 1.0 KB 0 B 618 B
⚪ GetCepInfoByAddressValidationError 1.0 KB 1.0 KB 0 B 620 B
⚪ addBusinessDays 6.8 KB 6.8 KB 0 B 2.8 KB
⚪ capitalize 2.5 KB 2.5 KB 0 B 1.3 KB
⚪ convertCurrencyToWords 2.8 KB 2.8 KB 0 B 1.5 KB
⚪ convertDateToWords 3.2 KB 3.2 KB 0 B 1.7 KB
⚪ convertLicensePlateToMercosul 1.3 KB 1.3 KB 0 B 807 B
⚪ convertNumberToWords 2.4 KB 2.4 KB 0 B 1.3 KB
⚪ differenceInBusinessDays 6.9 KB 6.9 KB 0 B 2.9 KB
⚪ formatBoleto 1.4 KB 1.4 KB 0 B 837 B
⚪ formatCEP 1.2 KB 1.2 KB 0 B 778 B
⚪ formatCNPJ 1.4 KB 1.4 KB 0 B 855 B
⚪ formatCPF 1.3 KB 1.3 KB 0 B 807 B
⚪ formatCaepf 1.3 KB 1.3 KB 0 B 787 B
⚪ formatCei 1.3 KB 1.3 KB 0 B 785 B
⚪ formatCep 1.2 KB 1.2 KB 0 B 778 B
⚪ formatCertidao 1.3 KB 1.3 KB 0 B 789 B
⚪ formatCnae 1.2 KB 1.2 KB 0 B 782 B
⚪ formatCnh 1.3 KB 1.3 KB 0 B 780 B
⚪ formatCno 1.3 KB 1.3 KB 0 B 786 B
⚪ formatCnpj 1.4 KB 1.4 KB 0 B 855 B
⚪ formatCns 1.3 KB 1.3 KB 0 B 780 B
⚪ formatCpf 1.3 KB 1.3 KB 0 B 807 B
⚪ formatCurrency 1.8 KB 1.8 KB 0 B 1.0 KB
⚪ formatIban 1.1 KB 1.1 KB 0 B 696 B
⚪ formatLegalNature 1.2 KB 1.2 KB 0 B 777 B
⚪ formatLicensePlate 1.2 KB 1.2 KB 0 B 738 B
⚪ formatNcm 1.2 KB 1.2 KB 0 B 780 B
⚪ formatNfeKey 1.3 KB 1.3 KB 0 B 783 B
⚪ formatPassport 1.0 KB 1.0 KB 0 B 643 B
⚪ formatPhone 2.8 KB 2.8 KB 0 B 1.5 KB
⚪ formatPis 1.3 KB 1.3 KB 0 B 781 B
⚪ formatProcessoJuridico 1.3 KB 1.3 KB 0 B 785 B
⚪ formatSuframa 1.3 KB 1.3 KB 0 B 815 B
⚪ formatVoterId 1.3 KB 1.3 KB 0 B 821 B
⚪ generateBoleto 2.1 KB 2.1 KB 0 B 1.2 KB
⚪ generateCNPJ 1.6 KB 1.6 KB 0 B 968 B
⚪ generateCPF 1.4 KB 1.4 KB 0 B 878 B
⚪ generateCep 984 B 984 B 0 B 610 B
⚪ generateCnh 1.4 KB 1.4 KB 0 B 829 B
⚪ generateCnpj 1.6 KB 1.6 KB 0 B 968 B
⚪ generateCpf 1.4 KB 1.4 KB 0 B 878 B
⚪ generateLegalNature 5.9 KB 5.9 KB 0 B 2.1 KB
⚪ generateLicensePlate 1.1 KB 1.1 KB 0 B 692 B
⚪ generatePassport 1.1 KB 1.1 KB 0 B 656 B
⚪ generatePhone 1.5 KB 1.5 KB 0 B 900 B
⚪ generatePis 1.2 KB 1.2 KB 0 B 744 B
⚪ generatePixPayload 6.3 KB 6.3 KB 0 B 2.8 KB
⚪ generateProcessoJuridico 1.4 KB 1.4 KB 0 B 870 B
⚪ generateRenavam 1.2 KB 1.2 KB 0 B 760 B
⚪ generateSuframa 1.3 KB 1.3 KB 0 B 809 B
⚪ generateVoterId 1.7 KB 1.7 KB 0 B 1021 B
⚪ getAddressInfoByCep 4.1 KB 4.1 KB 0 B 1.9 KB
⚪ getAreaCodeInfo 3.9 KB 3.9 KB 0 B 1.4 KB
⚪ getAreaCodesByState 1.6 KB 1.6 KB 0 B 917 B
⚪ getBankByCode 38.6 KB 38.6 KB 0 B 9.8 KB
⚪ getBankByIspb 38.6 KB 38.6 KB 0 B 9.8 KB
⚪ getBanks 38.4 KB 38.4 KB 0 B 9.6 KB
⚪ getBoletoInfo 3.1 KB 3.1 KB 0 B 1.6 KB
⚪ getCbo 119.1 KB 119.1 KB 0 B 30.7 KB
⚪ getCepInfoByAddress 2.7 KB 2.7 KB 0 B 1.4 KB
⚪ getCertidaoInfo 1.8 KB 1.8 KB 0 B 1.0 KB
⚪ getCfop 68.9 KB 68.9 KB 0 B 6.9 KB
⚪ getCities 154.3 KB 154.3 KB 0 B 49.9 KB
⚪ getCnae 93.9 KB 93.9 KB 0 B 21.2 KB
⚪ getCnpjInfo 1.8 KB 1.8 KB 0 B 1012 B
⚪ getCpfInfo 1.7 KB 1.7 KB 0 B 999 B
⚪ getFormatLicensePlate 1.1 KB 1.1 KB 0 B 692 B
⚪ getHolidays 6.1 KB 6.1 KB 0 B 2.6 KB
⚪ getIbanInfo 1.6 KB 1.6 KB 0 B 955 B
⚪ getLegalNature 6.3 KB 6.3 KB 0 B 2.3 KB
⚪ getLegalNatures 5.9 KB 5.9 KB 0 B 2.1 KB
⚪ getLegalNaturesByCategory 6.5 KB 6.5 KB 0 B 2.4 KB
⚪ getMunicipalities 156.4 KB 156.4 KB 0 B 50.3 KB
⚪ getMunicipality 154.9 KB 154.9 KB 0 B 50.3 KB
⚪ getMunicipalityByCode 156.5 KB 156.5 KB 0 B 50.4 KB
⚪ getNfeKeyInfo 2.7 KB 2.7 KB 0 B 1.5 KB
⚪ getPixKeyInfo 4.5 KB 4.5 KB 0 B 2.0 KB
⚪ getPixPayloadInfo 2.9 KB 2.9 KB 0 B 1.4 KB
🆕 getStateByCep — 4.5 KB new 1.5 KB
⚪ getStateByIbgeCode 3.2 KB 3.2 KB 0 B 1.1 KB
⚪ getStateCodeByName 3.2 KB 3.2 KB 0 B 1.1 KB
⚪ getStateNameByCode 3.1 KB 3.1 KB 0 B 1.0 KB
⚪ getStates 3.0 KB 3.0 KB 0 B 1017 B
⚪ getTimezoneByState 1.6 KB 1.6 KB 0 B 809 B
⚪ isBusinessDay 6.5 KB 6.5 KB 0 B 2.7 KB
⚪ isHoliday 6.4 KB 6.4 KB 0 B 2.7 KB
⚪ isValidBankAccount 7.4 KB 7.4 KB 0 B 2.8 KB
⚪ isValidBoleto 2.4 KB 2.4 KB 0 B 1.3 KB
⚪ isValidCEP 984 B 984 B 0 B 610 B
⚪ isValidCNPJ 1.6 KB 1.6 KB 0 B 914 B
⚪ isValidCPF 1.3 KB 1.3 KB 0 B 805 B
⚪ isValidCaepf 1.5 KB 1.5 KB 0 B 913 B
⚪ isValidCbo 119.2 KB 119.2 KB 0 B 30.7 KB
⚪ isValidCei 1.5 KB 1.5 KB 0 B 898 B
⚪ isValidCep 984 B 984 B 0 B 610 B
⚪ isValidCertidao 1.6 KB 1.6 KB 0 B 938 B
⚪ isValidCfop 68.9 KB 68.9 KB 0 B 6.9 KB
⚪ isValidCnae 94.0 KB 94.0 KB 0 B 21.2 KB
⚪ isValidCnh 1.4 KB 1.4 KB 0 B 856 B
⚪ isValidCno 1.5 KB 1.5 KB 0 B 900 B
⚪ isValidCnpj 1.6 KB 1.6 KB 0 B 914 B
⚪ isValidCns 1.5 KB 1.5 KB 0 B 925 B
⚪ isValidCpf 1.3 KB 1.3 KB 0 B 805 B
⚪ isValidCreditCard 1.4 KB 1.4 KB 0 B 868 B
⚪ isValidCsosn 1.2 KB 1.2 KB 0 B 737 B
⚪ isValidCst 1.8 KB 1.8 KB 0 B 1.0 KB
⚪ isValidEmail 1.0 KB 1.0 KB 0 B 622 B
⚪ isValidIE 5.7 KB 5.7 KB 0 B 2.1 KB
⚪ isValidIban 1.3 KB 1.3 KB 0 B 836 B
⚪ isValidIe 5.7 KB 5.7 KB 0 B 2.1 KB
⚪ isValidLandlinePhone 1.5 KB 1.5 KB 0 B 932 B
⚪ isValidLegalNature 5.8 KB 5.8 KB 0 B 2.1 KB
⚪ isValidLicensePlate 1.1 KB 1.1 KB 0 B 702 B
⚪ isValidMobilePhone 1.6 KB 1.6 KB 0 B 971 B
⚪ isValidNcm 114.2 KB 114.2 KB 0 B 24.6 KB
⚪ isValidNfeKey 2.7 KB 2.7 KB 0 B 1.5 KB
⚪ isValidPIS 1.2 KB 1.2 KB 0 B 784 B
⚪ isValidPassport 1.0 KB 1.0 KB 0 B 654 B
⚪ isValidPhone 2.6 KB 2.6 KB 0 B 1.3 KB
⚪ isValidPis 1.2 KB 1.2 KB 0 B 784 B
⚪ isValidPixKey 4.6 KB 4.6 KB 0 B 2.1 KB
⚪ isValidPixPayload 2.9 KB 2.9 KB 0 B 1.5 KB
⚪ isValidProcessoJuridico 1.3 KB 1.3 KB 0 B 787 B
⚪ isValidRegistroProfissional 1.6 KB 1.6 KB 0 B 964 B
⚪ isValidRenavam 1.3 KB 1.3 KB 0 B 814 B
⚪ isValidServicePhone 1.5 KB 1.5 KB 0 B 846 B
⚪ isValidSuframa 1.4 KB 1.4 KB 0 B 884 B
⚪ isValidVin 1.6 KB 1.6 KB 0 B 995 B
⚪ isValidVoterId 1.6 KB 1.6 KB 0 B 900 B
⚪ parseBoleto 1020 B 1020 B 0 B 634 B
⚪ parseCaepf 1003 B 1003 B 0 B 621 B
⚪ parseCbo 1002 B 1002 B 0 B 620 B
⚪ parseCei 1003 B 1003 B 0 B 619 B
⚪ parseCep 1002 B 1002 B 0 B 620 B
⚪ parseCertidao 1003 B 1003 B 0 B 620 B
⚪ parseCfop 1002 B 1002 B 0 B 620 B
⚪ parseCnae 1002 B 1002 B 0 B 620 B
⚪ parseCnh 1003 B 1003 B 0 B 621 B
⚪ parseCno 1003 B 1003 B 0 B 619 B
⚪ parseCnpj 1.1 KB 1.1 KB 0 B 669 B
⚪ parseCns 1003 B 1003 B 0 B 621 B
⚪ parseCpf 1003 B 1003 B 0 B 621 B
⚪ parseCurrency 1.4 KB 1.4 KB 0 B 881 B
⚪ parseIban 1.0 KB 1.0 KB 0 B 638 B
⚪ parseLegalNature 1002 B 1002 B 0 B 620 B
⚪ parseLicensePlate 1.0 KB 1.0 KB 0 B 638 B
⚪ parseNcm 1002 B 1002 B 0 B 620 B
⚪ parseNfeKey 1.0 KB 1.0 KB 0 B 659 B
⚪ parsePassport 1.0 KB 1.0 KB 0 B 637 B
⚪ parsePhone 1.1 KB 1.1 KB 0 B 707 B
⚪ parsePis 1003 B 1003 B 0 B 621 B
⚪ parseProcessoJuridico 1003 B 1003 B 0 B 621 B
⚪ parseSuframa 1.0 KB 1.0 KB 0 B 656 B
⚪ parseVoterId 1.0 KB 1.0 KB 0 B 649 B
⚪ removeAccents 953 B 953 B 0 B 593 B
⚪ subBusinessDays 6.9 KB 6.9 KB 0 B 2.9 KB
⚪ toStandardSchema 1.1 KB 1.1 KB 0 B 713 B
How this is measured

Every export is imported alone into an esbuild consumer bundle (minified, tree-shaken) built from the head and from the base of this pull request; the sizes are the resulting bundles, gzip is their gzipped size. 🔴 marks a regression: a pre-existing export that grew more than 20% and more than 256 B, or the bundle importing every pre-existing export growing more than 5%. 🟡 is growth under the threshold, 🟢 a decrease, ⚪ no change, 🆕 an export that does not exist on the base (never a regression), 🗑️ an export that was removed. An intentional increase is accepted with the tree-shaking: accepted label.

@codecov

codecov Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (7facc52) to head (8047b20).

Additional details and impacted files
@@                  Coverage Diff                   @@
##           claude/get-cnpj-info      #562   +/-   ##
======================================================
  Coverage                100.00%   100.00%           
======================================================
  Files                       192       194    +2     
  Lines                      2101      2109    +8     
  Branches                    619       621    +2     
======================================================
+ Hits                       2101      2109    +8     
Flag Coverage Δ
node 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hyanmandian
hyanmandian force-pushed the claude/get-state-by-cep branch from 8c13c81 to 2b00546 Compare September 22, 2026 05:01
@vercel

vercel Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
brazilian-utils Ready Ready Preview Sep 27, 2026 1:48am UTC

@hyanmandian
hyanmandian changed the base branch from main to claude/get-cnpj-info September 22, 2026 05:01
@pkg-pr-new

pkg-pr-new Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@brazilian-utils/brazilian-utils@562

commit: 8047b20

@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Pull request base or head changed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…range

Knowing the state of a CEP so far took a network call to a CEP API. The
Correios assign every state one or two ranges of CEPs, so the state can be
answered offline from a 30 row table: Amazonas, Distrito Federal and Goiás
have two ranges each, and 00000-000 to 00999-999 and 78900-000 to 78999-999
belong to no state and answer null.

The value goes through isValidCep and parseCep, and a number has to be a
non-negative integer, as in getStateByIbgeCode. The result is the same State
object the other state utils return.
The lookup walked the 27 states and re-scanned the 30 ranges for each
one, up to 810 comparisons for every call. The question is which range
holds the CEP, so the range table is the outer loop: at most 57
comparisons, and the shape reads like the sibling getStateByIbgeCode.

Also say in the docs that a range is the block the state owns and not a
promise that every CEP in it is in use, since 10000-000 to 10999-999
sits unused inside the range of São Paulo, and cover that block and the
shape of the table (ascending, no overlap, one inner gap) with tests.
… municipality table

getStateByCep reads its range table through a new internal,
_internals/find-cep-range, which validates and parses the CEP once and returns
the range that holds it, so the lookup is written once. The IBGE municipality
table moves from src/_internals/constants/cities.ts to municipalities.ts: it
holds municipalities (with their codes and states), not a list of city names,
and every util that reads it now imports it under that name. The generated file
is unchanged apart from its path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLkm9YrtAifc6XCLFVEsdH
@hyanmandian
hyanmandian force-pushed the claude/get-state-by-cep branch from 9a904ee to 8047b20 Compare September 27, 2026 01:47
@hyanmandian hyanmandian changed the title feat: add getStateByCep and getMunicipalityByCep, the state and municipality that own a CEP range feat(get-state-by-cep): add getStateByCep, the state that owns a CEP range Sep 27, 2026

This branch was successfully deployed

1 active deployment
Preview — 8047b208 Deployed Sep 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants